package plus.easydo.starter.oauth.server.serializer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import com.google.common.base.Preconditions;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import plus.easydo.starter.oauth.core.model.CustomizeUserDetails;
import plus.easydo.starter.oauth.server.model.OauthCode;
import plus.easydo.starter.redis.FastJson2RedisSerializer;

import java.nio.charset.StandardCharsets;


/**
 * redis存储json格式序列化反序列化工具类 继承redis序列化
 * @author yuzhanfeng
 */
public class FastJson2RedisTokenStoreSerializationStrategy<T> extends FastJson2RedisSerializer<T> implements RedisTokenStoreSerializationStrategy {

    private static final ParserConfig config = new ParserConfig();

    static {
        init();
    }

    public FastJson2RedisTokenStoreSerializationStrategy(Class<T> clazz) {
        super(clazz);
    }

    protected static void init() {
        //自定义oauth2序列化：DefaultOAuth2RefreshToken 没有setValue方法，会导致JSON序列化为null
        /*开启AutoType*/
        config.setAutoTypeSupport(true);
        /*自定义DefaultOauth2RefreshTokenSerializer反序列化*/
        config.putDeserializer(DefaultOAuth2RefreshToken.class, new DefaultOauth2RefreshTokenSerializer());
        /*自定义OAuth2Authentication反序列化*/
        config.putDeserializer(OAuth2Authentication.class, new Oauth2AuthenticationSerializer());
        //添加autotype白名单
        config.addAccept("club.gebilaoyu.cloud.oauth.");
        TypeUtils.addMapping("plus.easydo.starter.oauth.core.model.CustomizeUserDetails", CustomizeUserDetails.class);
        TypeUtils.addMapping("club.gebilaoyu.cloud.starter.model.OauthCode", OauthCode.class);

        config.addAccept("org.springframework.security.authentication.");
        TypeUtils.addMapping("org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
                UsernamePasswordAuthenticationToken.class);

        config.addAccept("org.springframework.security.oauth2.provider.");
        config.addAccept("org.springframework.security.oauth2.provider.client");
        TypeUtils.addMapping("org.springframework.security.oauth2.provider.OAuth2Authentication",
                OAuth2Authentication.class);
        TypeUtils.addMapping("org.springframework.security.oauth2.provider.client.BaseClientDetails",
                BaseClientDetails.class);

        config.addAccept("org.springframework.security.oauth2.common.");
        TypeUtils.addMapping("org.springframework.security.oauth2.common.DefaultOAuth2AccessToken", DefaultOAuth2AccessToken.class);
        TypeUtils.addMapping("org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken",
                DefaultExpiringOAuth2RefreshToken.class);

        config.addAccept("org.springframework.security.web.authentication.preauth");
        TypeUtils.addMapping("org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken",
                PreAuthenticatedAuthenticationToken.class);
    }


    @Override
    public <T> T deserialize(byte[] bytes, Class<T> aClass) {
        Preconditions.checkArgument(aClass != null,
                "clazz can't be null");
        if (bytes == null || bytes.length == 0) {
            return null;
        }

        try {
            return JSON.parseObject(new String(bytes, DEFAULT_CHARSET), aClass, config);
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    @Override
    public String deserializeString(byte[] bytes) {
        return new String(bytes, DEFAULT_CHARSET);
    }

    @Override
    public byte[] serialize(Object object) {
        return super.serializeObject(object);
    }

    @Override
    public byte[] serialize(String data) {
        if (data == null || data.length() == 0) {
            return new byte[0];
        }

        return data.getBytes(StandardCharsets.UTF_8);
    }
}
